package sluentities;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.io.Resources;
import entities.Param;
import entities.StateNode;


import java.io.*;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * StateMachineModelEntity和StateMachineModel的区别是前面对应ProcessEntity，后面对应Process
 */

@JsonInclude(JsonInclude.Include.NON_NULL)
public class StateMachineModelEntity{

    //root state
     StateNode root;

    //process list
     List<ProcessEntity> processes = new ArrayList<ProcessEntity>();

    //params list
     List<Param> params=new ArrayList<Param>();

    //command 命令
    List<Command> commands = new ArrayList<Command>();

    @JsonIgnore
    public static Map<String ,StateNode > stateNodeMap=new HashMap<String, StateNode>();

    public Param getParamFromParamListByName(String paramName){
        if (params==null)
            return null;
        for (Param param : params){
            if (param.getName().equals(paramName))
                return param;
        }
        return null;
    }

    public List<String> getAssignedVariableByParamName(String paramName){
        List<String> ret = new ArrayList<String>();
        if (params==null)
            return null;
        for (Param param : params){
            List<String> defautlFunctions = param.getDefaultFunction();
            for (String defautlFunction : defautlFunctions)
                if (defautlFunction.contains(paramName)){
                    ret.add(param.getName());
                    continue;
                }
        }
        return ret;
    }

    public StateMachineModelEntity(){

    }

    private void checkStateParamMap(StateNode stateNode){

        Map<String, Param> statementParamMap=new HashMap<String, Param>();

        for (Param param:params){
            Param paramNew = param;
            statementParamMap.put(param.getName(),paramNew);
        }

        for (String param:stateNode.getParams()){

            Param paramAdd = statementParamMap.get(param);
            if(paramAdd==null){
                System.err.println("Param "+param+" in node"+stateNode.getId()+" did not exist in Model");
                System.exit(-1);
            }

        }

    }


    private void generateStateNodeMap(StateNode stateNode){
        stateNodeMap.put(stateNode.getId(), stateNode);
        checkStateParamMap(stateNode);

        if (stateNode.getSubStateNode() != null){
            for (StateNode substate:stateNode.getSubStateNode())
                generateStateNodeMap(substate);
        }
    }

    @JsonCreator
    public StateMachineModelEntity(
                          @JsonProperty("root") StateNode root,
                          @JsonProperty("processes") List<ProcessEntity> processes,
                          @JsonProperty("params") List<Param> params,
                          @JsonProperty("commands") List<Command> commandList) {
        this.root = root;
        this.processes = processes != null ? processes : new ArrayList<ProcessEntity>();
        this.params = params != null ? params : new ArrayList<Param>() ;
        this.commands = commandList != null ? commandList : new ArrayList<Command>() ;
        generateStateNodeMap(root);

    }

    public static String readResource(ClassLoader cl, String path) {
        return readResource(cl, path, StandardCharsets.UTF_8);
    }

    public static String readResource(ClassLoader cl, String path, Charset charset) {
        try {
            return Resources.toString(cl.getResource(path), charset);
        } catch (IOException e) {
            e.printStackTrace();
            System.exit(1);
        }
        return "";
    }



    public static <T> List<T> deepCopy(List<T> src) throws IOException, ClassNotFoundException {
        ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
        ObjectOutputStream out = new ObjectOutputStream(byteOut);
        out.writeObject(src);

        ByteArrayInputStream byteIn = new ByteArrayInputStream(byteOut.toByteArray());
        ObjectInputStream in = new ObjectInputStream(byteIn);
        @SuppressWarnings("unchecked")
        List<T> dest = (List<T>) in.readObject();
        return dest;
    }




    public  static StateMachineModelEntity getModelFromInternelJson(String jsonName) {
        ObjectMapper mapper = new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        try {
            return mapper.readValue(new File(jsonName), StateMachineModelEntity.class);
        } catch (IOException e) {
            e.printStackTrace();
            System.exit(1);
            return null;
        }
    }

    public StateNode getRoot() {
        return root;
    }

    public void setRoot(StateNode root) {
        this.root = root;
    }

    public List<Param> getParams() {
        return params;
    }

    public void setParams(List<Param> params) {
        this.params = params;
    }

    public List<ProcessEntity> getProcesses() {
        return processes;
    }

    public void setProcesses(List<ProcessEntity> processes) {
        this.processes = processes;
    }

    public List<Command> getCommands() {
        return commands;
    }

    public void setCommands(List<Command> commands) {
        this.commands = commands;
    }

    public String toString() {
        return "StateMachineModel" + root.toString();

    }

}
